home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Celestin Apprentice 5
/
Apprentice-Release5.iso
/
Source Code
/
Libraries
/
3D Class Library
/
3dcl.cp
< prev
next >
Wrap
Text File
|
1996-06-27
|
29KB
|
1,298 lines
// =======================================================================
// 3D Class Library, © Xilex Group
// - ------------------------------------------------------------------- -
// Written by Dmitry Boldyrev
// =======================================================================
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "3dcl.h"
WORD matrix[9];
WORD *perspect;
world3d world;
viewPoint camera;
struct
{
WORD startx;
WORD endx;
WORD startcol;
WORD endcol;
WORD x1;
WORD y1;
WORD x2;
WORD y2;
} points[480];
extern UBYTE *thepic; // Holds the 256x256 texture image
extern Rect rBounds;
const char Onyx_Banner[] =
"3D Class Library by Onyx Media Group." \
"All rights reserved.";
#define MIN(a, b) (((a)<(b))?(a):(b))
#define MAX(a, b) (((a)>(b))?(a):(b))
// =======================================================================
// • dotProduct function definition •
// =======================================================================
WORD dotProduct(const vector3d *v1, const vector3d *v2)
{
return v1->x * v2->x + v1->y * v2->y + v1->z * v2->z;
}
static void initPerspect()
{
perspect = new WORD [32768];
for (WORD count = 0; count < 32768; count++)
perspect[count] = 1024 * 64 / (count + 1);
}
static void delPerspect()
{
delete[] perspect;
}
// =======================================================================
// • vertex class definitions •
// =======================================================================
vertex::vertex()
{
}
static inline void subVectors(const vector3d *a, const vector3d *b, vector3d *c)
{
c->x = a->x - b->x;
c->y = a->y - b->y;
c->z = a->z - b->z;
}
// =======================================================================
// project the point to the screen
// =======================================================================
void vertex::xform2d()
{
static vector3d vec;
subVectors(&xyz, &camera.view, &vec);
if (vec.z)
{
xy.x = ((vec.x<<10)/vec.z) + middle.h;
xy.y = middle.v - ((vec.y<<10)/vec.z);
} else
{
xy.x = vec.x;
xy.y = vec.y;
}
}
// =======================================================================
// zero the normal for this point (used for gouraud shading)
// =======================================================================
void vertex::zeroNormal()
{
normal.x = 0;
normal.y = 0;
normal.z = 0;
nCount = 0;
}
void vertex::setTo(WORD x, WORD y, WORD z, WORD inKind)
{
xyz.x = x;
xyz.y = y;
xyz.z = z;
mKind = inKind;
zeroNormal();
xform2d();
}
vertex::vertex(WORD x, WORD y, WORD z, WORD inKind)
{
setTo(x, y, z, inKind);
}
// =======================================================================
// translate (move) a point by (dX, dY, dZ)
// =======================================================================
void vertex::translate(WORD dX, WORD dY, WORD dZ)
{
}
// =======================================================================
// add (dX, dY, dZ) to the normal for this point
// =======================================================================
void vertex::addNormal(WORD dX, WORD dY, WORD dZ)
{
normal.x += dX;
normal.y += dY;
normal.z += dZ;
nCount ++;
}
// =======================================================================
// average the normal of this point
// =======================================================================
void vertex::avgNormal()
{
if (nCount)
{
normal.x /= nCount;
normal.y /= nCount;
normal.z /= nCount;
WORD d = SQRT(SQR(normal.x) + SQR(normal.y) + SQR(normal.z));
normal.x = (normal.x << 16) / d;
normal.y = (normal.y << 16) / d;
normal.z = (normal.z << 16) / d;
}
}
// =======================================================================
// rotate a point about its origin (faster method, *mat array
// used to store sin/cos data that remains constant for all
// points in a given object
// =======================================================================
void vertex::localRotate(matrix3d mat)
{
static vector3d local;
local.x = (mat[0][0] * xyz.x + mat[0][1] * xyz.y + mat[0][2] * xyz.z) >> 16;
local.y = (mat[1][0] * xyz.x + mat[1][1] * xyz.y + mat[1][2] * xyz.z) >> 16;
local.z = (mat[2][0] * xyz.x + mat[2][1] * xyz.y + mat[2][2] * xyz.z) >> 16;
xyz = local;
xform2d();
}
vertex::~vertex()
{
}
// =======================================================================
// • polygon class definitions •
// =======================================================================
polygon::polygon()
{
}
polygon::polygon(vertex *v1, vertex *v2, vertex *v3)
{
setTo(v1, v2, v3);
}
void polygon::setTo(vertex *v1, vertex *v2, vertex *v3)
{
register double i, j, k, d;
p1 = v1;
p2 = v2;
p3 = v3;
// Calculating the normal to the polygon
i = (((p2->xyz.y - p1->xyz.y) * (p3->xyz.z - p1->xyz.z)) -
((p2->xyz.z - p1->xyz.z) * (p3->xyz.y - p1->xyz.y)));
j = (((p2->xyz.z - p1->xyz.z) * (p3->xyz.x - p1->xyz.x)) -
((p2->xyz.x - p1->xyz.x) * (p3->xyz.z - p1->xyz.z)));
k = (((p2->xyz.x - p1->xyz.x) * (p3->xyz.y - p1->xyz.y)) -
((p2->xyz.y - p1->xyz.y) * (p3->xyz.x - p1->xyz.x)));
d = SQRT(SQR(i) + SQR(j) + SQR(k));
i = (i / d * 32768);
j = (j / d * 32768);
k = (k / d * 32768);
normal.setTo(i, j, k, isNormal);
}
// =======================================================================
// average Z value of a polygon, used for depth sorting
// =======================================================================
WORD polygon::calcAvgZ()
{
return (averageZ = 32767 + ((p1->xyz.z + p2->xyz.z + p3->xyz.z) / 3));
}
void polygon::setNewOrigin(vertex *p)
{
p1->setNewOrigin(p);
p2->setNewOrigin(p);
p3->setNewOrigin(p);
normal.setNewOrigin(p);
}
// =======================================================================
// this function should be called for all polygons in an object,
// following which point->avgNormals() should be called for all
// points in an object. The object3d->setGNormals function takes
// care of all of this...
// =======================================================================
void polygon::setGNormals()
{
p1->addNormal(normal.xyz.x, normal.xyz.y, normal.xyz.z);
p2->addNormal(normal.xyz.x, normal.xyz.y, normal.xyz.z);
p3->addNormal(normal.xyz.x, normal.xyz.y, normal.xyz.z);
}
void polygon::renderFlat()
{
}
void polygon::renderGouraudTexMap()
{
}
void polygon::renderWire()
{
::ForeColor(whiteColor);
::MoveTo(p1->xy.x, p1->xy.y);
::LineTo(p2->xy.x, p2->xy.y);
::LineTo(p3->xy.x, p3->xy.y);
::LineTo(p1->xy.x, p1->xy.y);
/*::ForeColor(greenColor);
::MoveTo(p1->xy.x, p1->xy.y);
::LineTo(normal.xy.x, normal.xy.y); */
}
void polygon::renderNone()
{
}
#if GENERATINGCFM
asm void limit_upoint(register upoint *p, register Rect *r);
#pragma parameter limit_upoint(__r3, __r4)
asm void limit_upoint(
register upoint *p,
register Rect *r)
{
lwz r5, p->x // r5 = p->x
lha r6, r->left // r6 = r->left
cmpw r5, r6 // p->x < r->left ?
bge @1 // NO
mr r5, r6 // r5 = r6
stw r6, p->x // YES
@1 lha r6, r->right // r6 = r->right
cmpw r5, r6 // p->x > r->right ?
ble @2 // NO
mr r5, r6 // r5 = r6
stw r6, p->x // YES
@2 lwz r5, p->y // r5 = p->y
lha r6, r->top // r6 = r->top
cmpw r5, r6 // p->y < r->top ?
bge @3 // NO
mr r5, r6 // r5 = r6
stw r6, p->y // YES
@3 lha r6, r->bottom // r6 = r->bottom
cmpw r5, r6 // p->y > r->bottom ?
ble @4 // NO
mr r5, r6 // r5 = r6
stw r6, p->y // YES
@4 blr
}
#else
static void limit_upoint(
register upoint *p,
register Rect *r)
{
if (p->x < r->left) {
p->x = r->left;
} else
if (p->x > r->right) {
p->x = r->right;
}
if (p->y < r->top) {
p->y = r->top;
} else
if (p->y > r->bottom) {
p->y = r->bottom;
}
}
#endif
void polygon::renderTexMap()
{
register WORD x, mx, xcoord, ycoord, xstep, ystep, y;
register upoint pTop, pMid, pBot;
register UBYTE *src, *dest;
if (p2->xy.y > p1->xy.y)
{
pTop = p1->xy; pBot = p2->xy;
} else
{
pTop = p2->xy; pBot = p1->xy;
}
if (pTop.y > p3->xy.y)
{
pTop = p3->xy;
if (p2->xy.y > p1->xy.y)
{
pMid = p1->xy; pBot = p2->xy;
} else
{
pMid = p2->xy; pBot = p1->xy;
}
} else
{
if (pBot.y > p3->xy.y)
{
pMid = p3->xy;
} else
{
pMid = pBot; pBot = p3->xy;
}
}
limit_upoint(&pTop, &rBounds);
limit_upoint(&pMid, &rBounds);
limit_upoint(&pBot, &rBounds);
// p3.x, p3.y, p3.c -> p1.x, p1.y, p1.c
#if !GENERATINGCFM
if (pBot.y - pTop.y)
{
#endif
x = pTop.x << 8;
mx = ((pBot.x - pTop.x) << 8) / (pBot.y - pTop.y);
xcoord = pTop.u << 8;
xstep = ((pBot.u - pTop.u) << 8) / (pBot.y - pTop.y);
ycoord = pTop.v << 8;
ystep = ((pBot.v - pTop.v) << 8) / (pBot.y - pTop.y);
for (y = pTop.y; y < pBot.y; y++)
{
points[y].startx = x >> 8;
points[y].x1 = xcoord >> 8;
points[y].y1 = ycoord >> 8;
points[y].endx = 0;
points[y].x2 = 0;
points[y].y2 = 0;
x += mx;
xcoord += xstep;
ycoord += ystep;
}
#if !GENERATINGCFM
}
#endif
// X1, p1.y, p1.c -> p2.x, p2.y, p2.c
#if !GENERATINGCFM
if (pMid.y - pTop.y)
{
#endif
x = pTop.x << 8;
mx = ((pMid.x - pTop.x) << 8) / (pMid.y - pTop.y);
xcoord = pTop.u << 8;
xstep = ((pMid.u - pTop.u) << 8) / (pMid.y - pTop.y);
ycoord = pTop.v << 8;
ystep = ((pMid.v - pTop.v) << 8) / (pMid.y - pTop.y);
for (y = pTop.y; y < pMid.y; y++)
{
if (points[y].startx)
{
points[y].endx = x >> 8;
points[y].x2 = xcoord >> 8;
points[y].y2 = ycoord >> 8;
} else
{
points[y].startx = x >> 8;
points[y].x1 = xcoord >> 8;
points[y].y1 = ycoord >> 8;
}
x += mx;
xcoord += xstep;
ycoord += ystep;
}
#if !GENERATINGCFM
}
#endif
// p2.x, p2.y, p2.c -> p3.x, p3.y, p3.c
#if !GENERATINGCFM
if (pBot.y - pMid.y)
{
#endif
x = pMid.x << 8;
mx = ((pBot.x - pMid.x) << 8) / (pBot.y - pMid.y);
xcoord = pMid.u << 8;
xstep = ((pBot.u - pMid.u) << 8) / (pBot.y - pMid.y);
ycoord = pMid.v << 8;
ystep = ((pBot.v - pMid.v) << 8) / (pBot.y - pMid.y);
for (y = pMid.y; y < pBot.y; y++)
{
if (points[y].startx)
{
points[y].endx = x >> 8;
points[y].x2 = xcoord >> 8;
points[y].y2 = ycoord >> 8;
} else
{
points[y].startx = x >> 8;
points[y].x1 = xcoord >> 8;
points[y].y1 = ycoord >> 8;
}
x += mx;
xcoord += xstep;
ycoord += ystep;
}
#if !GENERATINGCFM
}
#endif
// Do the drawing
for (y = pTop.y; y < pBot.y; y++)
{
mx = points[y].endx - points[y].startx;
if (mx)
{
if (mx < 0)
{
x = points[y].startx; points[y].startx = points[y].endx; points[y].endx = x;
x = points[y].x1; points[y].x1 = points[y].x2; points[y].x2 = x;
x = points[y].y1; points[y].y1 = points[y].y2; points[y].y2 = x;
mx = -mx;
}
xcoord = points[y].x1 << 8;
xstep = ((points[y].x2 - points[y].x1) << 8) / mx;
ycoord = points[y].y1 << 8;
ystep = ((points[y].y2 - points[y].y1) << 8) / mx;
dest = (UBYTE*)baseAddr + y * rowBytes + points[y].startx;
for (x = mx; x >= 0; x--)
{
*dest++ = thepic[(ycoord & 0xFFFFFF00) + (xcoord>>8)];
xcoord += xstep;
ycoord += ystep;
}
}
}
}
void polygon::renderGouraud()
{
register WORD x, mx, c, mc, y;
register upoint pTop, pMid, pBot;
register Ptr base;
/* if ((dotProduct(&p1->normal, &camera.view) <= 0) &&
(dotProduct(&p2->normal, &camera.view) <= 0) &&
(dotProduct(&p3->normal, &camera.view) <= 0))
return;
*/
p1->xy.c = ABS(dotProduct(&p1->normal, &camera.light)) >> 26;
p2->xy.c = ABS(dotProduct(&p2->normal, &camera.light)) >> 26;
p3->xy.c = ABS(dotProduct(&p3->normal, &camera.light)) >> 26;
if (p2->xy.y > p1->xy.y)
{
pTop = p1->xy; pBot = p2->xy;
} else
{
pTop = p2->xy; pBot = p1->xy;
}
if (pTop.y > p3->xy.y)
{
pTop = p3->xy;
if (p2->xy.y > p1->xy.y)
{
pMid = p1->xy; pBot = p2->xy;
} else
{
pMid = p2->xy; pBot = p1->xy;
}
} else
{
if (pBot.y > p3->xy.y)
{
pMid = p3->xy;
} else
{
pMid = pBot; pBot = p3->xy;
}
}
limit_upoint(&pTop, &rBounds);
limit_upoint(&pMid, &rBounds);
limit_upoint(&pBot, &rBounds);
// p3.x, p3.y, p3.c -> p1.x, p1.y, p1.c
#if !GENERATINGCFM
if (pBot.y - pTop.y)
{
#endif
x = pTop.x << 16;
mx = ((pBot.x - pTop.x) << 16) / (pBot.y - pTop.y);
c = pTop.c << 16;
mc = ((pBot.c - pTop.c) << 16) / (pBot.y - pTop.y);
for (y = pTop.y; y < pBot.y; y++)
{
points[y].startx = x >> 16;
points[y].startcol = c >> 16;
points[y].endx = points[y].endcol = 0;
x += mx;
c += mc;
}
#if !GENERATINGCFM
}
#endif
// X1, p1.y, p1.c -> p2.x, p2.y, p2.c
#if !GENERATINGCFM
if (pMid.y - pTop.y)
{
#endif
x = pTop.x << 16;
mx = ((pMid.x - pTop.x) << 16) / (pMid.y - pTop.y);
c = pTop.c << 16;
mc = ((pMid.c - pTop.c) << 16) / (pMid.y - pTop.y);
for (y = pTop.y; y < pMid.y; y++)
{
if (points[y].startx)
{
points[y].endx = x >> 16;
points[y].endcol = c >> 16;
} else
{
points[y].startx = x >> 16;
points[y].startcol = c >> 16;
}
x += mx;
c += mc;
}
#if !GENERATINGCFM
}
#endif
// p2.x, p2.y, p2.c -> p3.x, p3.y, p3.c
#if !GENERATINGCFM
if (pBot.y - pMid.y)
{
#endif
x = pMid.x << 16;
mx = ((pBot.x - pMid.x) << 16) / (pBot.y - pMid.y);
c = pMid.c << 16;
mc = ((pBot.c - pMid.c) << 16) / (pBot.y - pMid.y);
for (y = pMid.y; y < pBot.y; y++)
{
//if ((y >= 0) & (y < 480))
if (points[y].startx)
{
points[y].endx = x >> 16;
points[y].endcol = c >> 16;
} else
{
points[y].startx = x >> 16;
points[y].startcol = c >> 16;
}
x += mx;
c += mc;
}
#if !GENERATINGCFM
}
#endif
// Do the drawing
register WORD start_x, end_x, start_col, end_col;
base = baseAddr + pTop.y * rowBytes;
for (y = pTop.y; y < pBot.y; y++)
{
start_x = points[y].startx; //xTop
end_x = points[y].endx; //xBot
#if !GENERATINGCFM
if (end_x - start_x)
{
#endif
start_col = points[y].startcol;
end_col = points[y].endcol;
if (end_x < start_x)
{
start_x = end_x; end_x = points[y].startx;
start_col = end_col; end_col = points[y].startcol;
}
c = start_col << 16;
mc = ((end_col - start_col) << 16) / (end_x - start_x);
for (x = start_x; x < end_x; x++)
{
*(base+x) = c >> 16;
c += mc;
}
base += rowBytes;
#if !GENERATINGCFM
}
#endif
}
}
void polygon::calcThetaPhi(vector3d v, WORD &r_theta, WORD &r_phi)
{
register double x, y, z, r, rho, theta, phi;
x = normal.xyz.x / 65536.0;
y = normal.xyz.y / 65536.0;
z = normal.xyz.z / 65536.0;
r = SQRT(SQR(x) + SQR(y));
rho = SQRT(SQR(r) + SQR(z));
phi = acos(z / r);
theta = asin(y / r);
r_theta = (theta / PI) * 65536.0;
r_phi = (phi / PI) * 65536.0;
}
#define WRAP(x) ABS(x % 256)
void polygon::applyTexture(WORD width, WORD height, WORD depth)
{
p1->xy.u = WRAP((p1->xyz.x<<8)/width);
p1->xy.v = WRAP((p1->xyz.y<<8)/height);
p2->xy.u = WRAP((p2->xyz.x<<8)/width);
p2->xy.v = WRAP((p2->xyz.y<<8)/height);
p3->xy.u = WRAP((p3->xyz.x<<8)/width);
p3->xy.v = WRAP((p3->xyz.y<<8)/height);
}
// ====================================================================
// must be called to initialize rendering proc
// ====================================================================
void polygon::renderPoly(BYTE method)
{
switch (method)
{
case sGouraudTexMap:
renderGouraudTexMap();
break;
case sTexMap:
renderTexMap();
break;
case sGouraud:
renderGouraud();
break;
case sFlat:
renderFlat();
break;
case sWire:
renderWire();
break;
default:
break;
}
}
polygon::~polygon()
{
}
// ====================================================================
// • object3d class definitions •
// ====================================================================
object3d::object3d()
{
xDeg = 0;
yDeg = 0;
zDeg = 0;
}
WORD object3d::getPointNum(vertex *p)
{
register vertex *pt3d;
count = 0;
points.GoToFirstNode();
while (pt3d = (vertex*) points.GetNextNode())
{
if (pt3d == p)
return count;
count++;
}
return -1;
}
vertex *object3d::getPointAddr(WORD pNum)
{
register WORD count;
register vertex *pt3d;
count = 0;
points.GoToFirstNode();
while (pt3d = (vertex*) points.GetNextNode())
{
if (count == pNum)
return pt3d;
count ++;
}
return 0;
}
// =======================================================================
// binary format save routine
// =======================================================================
void object3d::save(char *fname)
{
FILE *fp = ::fopen(fname, "wb");
objFileHeader header;
// assumes fp has already been opened, so we can store multiple objects
// in the same file
header.numPoints = points.NumNodes();
header.numPolys = polys.NumNodes();
fwrite(&header, sizeof(objFileHeader), 1, fp);
vertex *pt3d;
points.GoToFirstNode();
while (pt3d = (vertex*) points.GetNextNode())
{
fwrite(&pt3d->xyz, sizeof(vector3d), 1, fp);
}
polygon* poly;
polyRec pRec;
polys.GoToFirstNode();
while (poly = (polygon*) polys.GetNextNode())
{
pRec.p1 = getPointNum(poly->p1);
pRec.p2 = getPointNum(poly->p2);
pRec.p3 = getPointNum(poly->p3);
pRec.color = 0xFF;
fwrite(&pRec, sizeof(polyRec), 1, fp);
}
fclose(fp);
}
void object3d::addLocalPoint(vertex *p)
{
// p->globalXform2origin(origin);
points.AppendNode(p);
}
// =======================================================================
// add a point whose coordinates are given as local to the origin of
// this object to this objects list of points
// =======================================================================
void object3d::addLocalPoint(WORD x, WORD y, WORD z, WORD inKind)
{
addLocalPoint(new vertex(x, y, z, inKind));
}
// ====================================================================
// add a local polygon (whose vertices are local points)
// ====================================================================
void object3d::addLocalPoly(polygon *pg)
{
polys.AppendNode(pg);
addLocalPoint(&pg->normal);
}
void object3d::addLocalPoly(WORD p1, WORD p2, WORD p3)
{
addLocalPoly(new polygon(getPointAddr(p1), getPointAddr(p2), getPointAddr(p3)));
}
// =======================================================================
// binary format load routine
// =======================================================================
void object3d::load(char *fname)
{
WORD numPoints, numPolys;
FILE *fp = ::fopen(fname, "rb");
objFileHeader header;
// free old data
points.FreeNodes();
polys.FreeNodes();
// assumes fp has already been opened, so we can store multiple objects
// in the same file
fread(&header, sizeof(objFileHeader), 1, fp);
numPoints = header.numPoints;
numPolys = header.numPolys;
vector3d ptRec;
for (count = 0; count < numPoints; count++)
{
fread(&ptRec, sizeof(vector3d), 1, fp);
addLocalPoint(ptRec.x, ptRec.y, ptRec.z, isVertex);
}
polyRec pRec;
for (count = 0; count < numPolys; count++)
{
fread(&pRec, sizeof(polyRec), 1, fp);
addLocalPoly(pRec.p1, pRec.p2, pRec.p3);
}
fclose(fp);
}
void object3d::importASC(char *fname)
{
register FILE *inFile;
register WORD tempInt, vertices, faces, // Considering only 2i model
count, tempA, tempB, tempC;
register char tempChar;
register float tempX, tempY, tempZ;
register char tempStr[255];
inFile = fopen(fname, "rt");
if (inFile == NULL)
return;
while (strncmp(tempStr, "Vertices", 8))
{
fscanf(inFile, "%s", tempStr);
if (feof(inFile))
return;
}
tempChar = fgetc(inFile);
fscanf(inFile, "%d", &vertices);
while (strncmp(tempStr, "Faces", 5))
{
fscanf(inFile, "%s", tempStr);
if (feof(inFile))
return;
}
tempChar = fgetc(inFile);
fscanf(inFile, "%d", &faces);
while (strncmp(tempStr, "Vertex", 6))
{
fscanf(inFile, "%s", tempStr);
if (feof(inFile))
return;
}
while (strncmp(tempStr, "list:", 5))
{
fscanf(inFile, "%s", tempStr);
if (feof(inFile))
return;
}
for (count = 0; count < vertices; count++)
{
while (strncmp(tempStr, "Vertex", 6))
{
fscanf(inFile, "%s", tempStr);
if (feof(inFile))
return;
}
fscanf(inFile, "%d", &tempInt);
fscanf(inFile, "%s", tempStr);
fscanf(inFile, "%s", tempStr);
fscanf(inFile, "%f", &tempX);
fscanf(inFile, "%s", tempStr);
fscanf(inFile, "%f", &tempY);
fscanf(inFile, "%s", tempStr);
fscanf(inFile, "%f", &tempZ);
addLocalPoint(tempX, tempY, tempZ, isVertex);
}
while (strncmp(tempStr, "Face", 4))
{
fscanf(inFile, "%s", tempStr);
if (feof(inFile))
return;
}
while (strncmp(tempStr, "list", 4))
{
fscanf(inFile, "%s", tempStr);
if (feof(inFile))
return;
}
for (count = 0; count < faces; count++)
{
while (strncmp(tempStr, "Face", 4))
{
fscanf(inFile, "%s", tempStr);
if (feof(inFile))
return;
}
fscanf(inFile, "%d", &tempInt);
fscanf(inFile, "%s", tempStr);
while (fgetc(inFile) != 'A');
fgetc(inFile); // get the ':' character
fscanf(inFile, "%d", &tempA); // get value for vertex A
while (fgetc(inFile) != 'B');
fgetc(inFile);
fscanf(inFile, "%d", &tempB);
while (fgetc(inFile) != 'C');
fgetc(inFile);
fscanf(inFile, "%d", &tempC);
addLocalPoly(tempA, tempB, tempC);
}
fclose(inFile);
}
#define koeff 100
void object3d::importV3D(char *fname)
{
register FILE *inFile;
register WORD vertices, faces, count, tempA,
tempB, tempC, tempD, color;
register SINGLE tempX, tempY, tempZ;
register char tempStr[255];
inFile = fopen(fname, "rt");
if (inFile == NULL)
return;
fscanf(inFile, "%s", tempStr);
if (strncmp(tempStr, "3DG1", 4) != 0)
return;
fscanf(inFile, "%d", &vertices);
for (count = 0; count < vertices; count++)
{
fscanf(inFile, "%f %f %f", &tempX, &tempY, &tempZ);
addLocalPoint(tempX*koeff, tempY*koeff, tempZ*koeff, isVertex);
}
for (count = 0; ; count++)
{
fscanf(inFile, "%d", &faces);
switch (faces)
{
case 1:
fscanf(inFile, "%d %d", &tempA, &color);
addLocalPoly(tempA, tempA, tempA);
break;
case 2:
fscanf(inFile, "%d %d %d", &tempA, &tempB, &color);
addLocalPoly(tempA, tempB, tempA);
break;
case 3:
fscanf(inFile, "%d %d %d %d", &tempA, &tempB, &tempC, &color);
addLocalPoly(tempA, tempB, tempC);
break;
case 4:
fscanf(inFile, "%d %d %d %d %d", &tempA, &tempB, &tempC, &tempD, &color);
addLocalPoly(tempA, tempB, tempC);
addLocalPoly(tempC, tempD, tempA);
break;
default:
fgets(tempStr, 255, inFile);
break;
}
if (feof(inFile))
break;
}
fclose(inFile);
}
// =======================================================================
// rotate this object about it's origin
// =======================================================================
void object3d::localRotate()
{
register vertex *pt3d;
points.GoToFirstNode();
while (pt3d = (vertex*) points.GetNextNode())
pt3d->localRotate(mat);
}
void object3d::applyTexture()
{
polygon *poly;
WORD width, height, depth;
calcBounds(width, height, depth);
polys.GoToFirstNode();
while (poly = (polygon*) polys.GetNextNode())
poly->applyTexture(width, height, depth);
}
void object3d::calcBounds(WORD &width, WORD &height, WORD &depth)
{
register vertex *pt3d;
register WORD minX, minY, minZ,
maxX, maxY, maxZ;
minX = minY = minZ = 32767;
maxX = maxY = maxZ = -32767;
points.GoToFirstNode();
while (pt3d = (vertex*) points.GetNextNode())
{
if (pt3d->mKind == isVertex)
{
minX = MIN(minX, pt3d->xyz.x);
minY = MIN(minY, pt3d->xyz.y);
minZ = MIN(minZ, pt3d->xyz.z);
maxX = MAX(maxX, pt3d->xyz.x);
maxY = MAX(maxY, pt3d->xyz.y);
maxZ = MAX(maxZ, pt3d->xyz.z);
}
}
width = maxX - minX; height = maxY - minY; depth = maxZ - minZ;
}
#define TORAD(x) ((x) * PI / 180.0)
void object3d::makeRotMatrix(WORD Xrot, WORD Yrot, WORD Zrot)
{
register double cXrot, cYrot, cZrot,
sXrot, sYrot, sZrot;
cXrot = cos(TORAD(Xrot)); cYrot = cos(TORAD(Yrot)); cZrot = cos(TORAD(Zrot));
sXrot = sin(TORAD(Xrot)); sYrot = sin(TORAD(Yrot)); sZrot = sin(TORAD(Zrot));
mat[0][0] = 65536 * (cYrot * cZrot);
mat[0][1] = 65536 * (cZrot * sXrot * sYrot - sZrot * cXrot);
mat[0][2] = 65536 * (-(cXrot * cZrot * sYrot + sXrot * sZrot));
mat[1][0] = 65536 * (sZrot * cYrot);
mat[1][1] = 65536 * (sXrot * sYrot * sZrot + cXrot * cZrot);
mat[1][2] = 65536 * (-(sYrot * sZrot * cXrot) + sXrot * cZrot);
mat[2][0] = 65536 * (sYrot);
mat[2][1] = 65536 * (-sXrot * cYrot);
mat[2][2] = 65536 * (cXrot * cYrot);
}
// =======================================================================
// translate (move) this object
// =======================================================================
void object3d::translate(WORD dX, WORD dY, WORD dZ)
{
register vertex *pt3d;
points.GoToFirstNode();
while (pt3d = (vertex*) points.GetNextNode())
pt3d->translate(dX, dY, dZ);
}
// =======================================================================
// radix sort, peforms linear-time sorting on the polygons
// =======================================================================
void object3d::sortPlanes()
{
register polygon *poly;
register UBYTE index;
register WORD count;
// Consider only the least significant radix
polys.GoToFirstNode();
while (poly = (polygon*) polys.GetNextNode())
{
index = (poly->calcAvgZ()) & 0x00FF;
stack1[index].push(poly);
}
// Move shtuff from stack1 to stack2, considering second least
// significant radix
for (count = 255; count >= 0; count--)
{
while (!stack1[count].empty())
{
poly = (polygon*)stack1[count].pop();
index = (poly->averageZ & 0xFF00) >> 8;
stack2[index].push(poly);
}
}
// Rebuild the list
polys.ForgetNodes();
for (count = 0; count <= 255; count++)
{
while (!stack2[count].empty())
{
polys.AppendNode(stack2[count].pop());
}
}
}
// =======================================================================
// have this object set up its normal vectors needed for gouraud shading
// =======================================================================
void object3d::setGNormals()
{
register polygon *poly;
polys.GoToFirstNode();
while (poly = (polygon*) polys.GetNextNode())
poly->setGNormals();
vertex *pt3d;
points.GoToFirstNode();
while (pt3d = (vertex*) points.GetNextNode())
{
pt3d->avgNormal();
}
}
void object3d::zeroGNormals()
{
register vertex *pt3d;
points.GoToFirstNode();
while (pt3d = (vertex*) points.GetNextNode())
{
pt3d->zeroNormal();
}
}
// =======================================================================
// universal display function that utilizes each polygon's shading
// and facing data. This allows you to combine gouraud, flat, and
// nonshaded polygons in the same object.
// =======================================================================
void object3d::display()
{
sortPlanes();
register polygon *poly;
polys.GoToFirstNode();
while (poly = (polygon*) polys.GetNextNode())
poly->renderPoly(sGouraud);
}
object3d::~object3d()
{
}
// =======================================================================
// • world3d class definitons •
// =======================================================================
world3d::world3d()
{
initSinCos();
//initPerspect();
}
world3d::~world3d()
{
//delPerspect();
}
void world3d::insertObject(object3d *object)
{
objects.AppendNode(object);
}
void world3d::deleteObject(object3d *object, BOOL doDisposeIt)
{
objects.RemoveNode(object, doDisposeIt);
}
void world3d::globalRotate(WORD tX, WORD tY, WORD tZ)
{
}
void world3d::draw()
{
register object3d *object;
objects.GoToFirstNode();
while (object = (object3d*) objects.GetNextNode())
object->display();
}
viewPoint::viewPoint()
{
location.x = 0;
location.y = 0;
location.z = 0;
light.x = 0;
light.y = 0;
light.z = 32767;
view.x = 0;
view.y = 0;
view.z = 2300;
}
viewPoint::~viewPoint()
{
}